home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / screenshot.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-05-16  |  13.4 KB  |  507 lines

  1. /*  
  2.  *  ScreenShot plug-in
  3.  *  Copyright 1998-2000 Sven Neumann <sven@gimp.org>
  4.  *
  5.  *  Any suggestions, bug-reports or patches are very welcome.
  6.  * 
  7.  *  This plug-in uses the X-utility xwd to grab an image from the screen
  8.  *  and the xwd-plug-in created by Peter Kirchgessner (pkirchg@aol.com)
  9.  *  to load this image into the gimp.
  10.  *  Hence its nothing but a simple frontend to those utilities.
  11.  */
  12.  
  13. /* The GIMP -- an image manipulation program
  14.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  15.  *
  16.  * This program is free software; you can redistribute it and/or modify
  17.  * it under the terms of the GNU General Public License as published by
  18.  * the Free Software Foundation; either version 2 of the License, or
  19.  * (at your option) any later version.
  20.  *
  21.  * This program is distributed in the hope that it will be useful,
  22.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24.  * GNU General Public License for more details.
  25.  *
  26.  * You should have received a copy of the GNU General Public License
  27.  * along with this program; if not, write to the Free Software
  28.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  29.  */
  30.  
  31. #include "config.h"
  32.  
  33. #include <stdio.h>
  34. #include <errno.h>
  35. #include <sys/wait.h>
  36. #include <unistd.h>
  37. #ifdef __EMX__
  38. #include <process.h>
  39. #endif
  40.  
  41. #include <gtk/gtk.h>
  42.  
  43. #include <libgimp/gimp.h>
  44. #include <libgimp/gimpui.h>
  45.  
  46. #include "libgimp/stdplugins-intl.h"
  47.  
  48.  
  49. /* Defines */
  50. #define PLUG_IN_NAME        "extension_screenshot"
  51.  
  52. #ifndef XWD
  53. #define XWD "xwd"
  54. #endif
  55.  
  56. typedef struct
  57. {
  58.   gboolean   root;
  59.   gchar     *window_id;
  60.   guint      delay;
  61.   gboolean   decor;
  62. } ScreenShotValues;
  63.  
  64. static ScreenShotValues shootvals = 
  65.   FALSE,     /* root window */
  66.   NULL,      /* window ID */
  67.   0,         /* delay */
  68.   TRUE,      /* decorations */
  69. };
  70.  
  71.  
  72. static void      query (void);
  73. static void      run   (gchar      *name,
  74.             gint        nparams,
  75.             GimpParam  *param, 
  76.             gint       *nreturn_vals,
  77.             GimpParam **return_vals);
  78.  
  79. static void      shoot                (void);
  80. static gboolean  shoot_dialog         (void);
  81. static void      shoot_ok_callback    (GtkWidget *widget, 
  82.                        gpointer   data);
  83. static void      shoot_delay          (gint32     delay);
  84. static gint      shoot_delay_callback (gpointer   data);
  85.  
  86.  
  87. /* Global Variables */
  88. GimpPlugInInfo PLUG_IN_INFO =
  89. {
  90.   NULL,  /* init_proc  */
  91.   NULL,     /* quit_proc  */
  92.   query, /* query_proc */
  93.   run    /* run_proc   */
  94. };
  95.  
  96. /* the image that will be returned */
  97. gint32    image_ID = -1;
  98.  
  99. gboolean  run_flag = FALSE;
  100.  
  101.  
  102. /* Functions */
  103.  
  104. MAIN ()
  105.  
  106. static void
  107. query (void)
  108. {
  109.   static GimpParamDef args[] =
  110.   {
  111.     { GIMP_PDB_INT32,  "run_mode",  "Interactive, non-interactive" },
  112.     { GIMP_PDB_INT32,  "root",      "Root window { TRUE, FALSE }" },
  113.     { GIMP_PDB_STRING, "window_id", "Window id" }
  114.   };
  115.   static gint nargs = sizeof (args) / sizeof (args[0]);
  116.  
  117.   static GimpParamDef return_vals[] =
  118.   {
  119.     { GIMP_PDB_IMAGE, "image", "Output image" }
  120.   };
  121.   static gint nreturn_vals = sizeof (return_vals) / sizeof (return_vals[0]);
  122.  
  123.   gimp_install_procedure (PLUG_IN_NAME,
  124.               "Creates a screenshot of a single window or the whole screen",
  125.               "This extension serves as a simple frontend to the "
  126.               "X-window utility xwd and the xwd-file-plug-in. "
  127.               "After specifying some options, xwd is called, the "
  128.               "user selects a window, and the resulting image is "
  129.               "loaded into the gimp. Alternatively the whole "
  130.               "screen can be grabbed. When called non-interactively "
  131.               "it may grab the root window or use the window-id "
  132.               "passed as a parameter.",
  133.               "Sven Neumann <sven@gimp.org>",
  134.               "1998 - 2000",
  135.               "v0.9.5 (2000/10/29)",
  136.               N_("<Toolbox>/File/Acquire/Screen Shot..."),
  137.               NULL,
  138.               GIMP_EXTENSION,        
  139.               nargs, nreturn_vals,
  140.               args, return_vals);
  141. }
  142.  
  143. static void 
  144. run (gchar      *name,
  145.      gint        nparams,
  146.      GimpParam  *param,
  147.      gint       *nreturn_vals,
  148.      GimpParam **return_vals)
  149. {
  150.   /* Get the runmode from the in-parameters */
  151.   GimpRunModeType run_mode = param[0].data.d_int32;    
  152.  
  153.   /* status variable, use it to check for errors in invocation usualy only 
  154.    * during non-interactive calling
  155.    */
  156.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;     
  157.  
  158.   /* always return at least the status to the caller. */
  159.   static GimpParam values[2];
  160.  
  161.   /* initialize the return of the status */     
  162.   values[0].type          = GIMP_PDB_STATUS;
  163.   values[0].data.d_status = status;
  164.   *nreturn_vals = 1;
  165.   *return_vals  = values;
  166.  
  167.   /* how are we running today? */
  168.   switch (run_mode)
  169.     {
  170.     case GIMP_RUN_INTERACTIVE:
  171.       /* Possibly retrieve data from a previous run */
  172.       gimp_get_data (PLUG_IN_NAME, &shootvals);
  173.       shootvals.window_id = NULL;
  174.  
  175.       INIT_I18N_UI ();
  176.  
  177.      /* Get information from the dialog */
  178.       if (!shoot_dialog ())
  179.     status = GIMP_PDB_EXECUTION_ERROR;
  180.       break;
  181.  
  182.     case GIMP_RUN_NONINTERACTIVE:
  183.       if (nparams == 3) 
  184.     {
  185.       shootvals.root      = param[1].data.d_int32;
  186.       shootvals.window_id = (gchar*) param[2].data.d_string;
  187.       shootvals.delay     = 0;
  188.       shootvals.decor     = FALSE;
  189.     }
  190.       else
  191.     status = GIMP_PDB_CALLING_ERROR;
  192.       break;
  193.  
  194.     case GIMP_RUN_WITH_LAST_VALS:
  195.       /* Possibly retrieve data from a previous run */
  196.       gimp_get_data (PLUG_IN_NAME, &shootvals);
  197.       break;
  198.  
  199.     default:
  200.       break;
  201.     }
  202.  
  203.   if (status == GIMP_PDB_SUCCESS)
  204.     {
  205.       if (shootvals.delay > 0)
  206.     shoot_delay (shootvals.delay);
  207.       /* Run the main function */
  208.       shoot ();
  209.  
  210.       status = (image_ID != -1) ? GIMP_PDB_SUCCESS : GIMP_PDB_EXECUTION_ERROR;
  211.     }
  212.  
  213.   if (status == GIMP_PDB_SUCCESS)
  214.     {
  215.       if (run_mode == GIMP_RUN_INTERACTIVE)
  216.     {
  217.       /* Store variable states for next run */
  218.       gimp_set_data (PLUG_IN_NAME, &shootvals, sizeof (ScreenShotValues));
  219.       /* display the image */
  220.       gimp_display_new (image_ID);
  221.     }
  222.       /* set return values */
  223.       *nreturn_vals = 2;
  224.       values[1].type = GIMP_PDB_IMAGE;
  225.       values[1].data.d_image = image_ID;
  226.     }
  227.  
  228.   values[0].data.d_status = status; 
  229. }
  230.  
  231.  
  232. /* The main ScreenShot function */
  233. static void 
  234. shoot (void)
  235. {
  236.   GimpParam *params;
  237.   gint       retvals;
  238.   gchar     *tmpname;
  239.   gchar     *xwdargv[7];    /*  need a maximum of 7 arguments to xwd  */
  240.   gdouble    xres, yres;
  241.   gint       pid;
  242.   gint       wpid;
  243.   gint       status;
  244.   gint       i = 0;
  245.  
  246.   /*  get a temp name with the right extension  */
  247.   tmpname = gimp_temp_name ("xwd");
  248.  
  249.   /*  construct the xwd arguments  */
  250.   xwdargv[i++] = XWD;
  251.   xwdargv[i++] = "-out";
  252.   xwdargv[i++] = tmpname;
  253.   if ( shootvals.root == TRUE )
  254.     xwdargv[i++] = "-root";
  255.   else 
  256.     {
  257.       if (shootvals.decor == TRUE )
  258.     xwdargv[i++] = "-frame";
  259.       if (shootvals.window_id != NULL)
  260.     {
  261.       xwdargv[i++] = "-id";
  262.       xwdargv[i++] = shootvals.window_id;
  263.     }
  264.     }
  265.   xwdargv[i] = NULL;
  266.   
  267. #ifndef __EMX__
  268.   /*  fork off a xwd process  */
  269.   if ((pid = fork ()) < 0)
  270.     {
  271.       g_message ("screenshot: fork failed: %s\n", g_strerror (errno));
  272.       return;
  273.     }
  274.   else if (pid == 0)
  275.     {
  276.       execvp (XWD, xwdargv);
  277.       /*  What are we doing here? exec must have failed  */
  278.       g_message ("screenshot: exec failed: xwd: %s\n", g_strerror (errno));
  279.       return;
  280.     }
  281.   else
  282. #else /* __EMX__ */
  283.   pid = spawnvp (P_NOWAIT, XWD, xwdargv);
  284.   if (pid == -1)
  285.     {
  286.       g_message ("screenshot: spawn failed: %s\n", g_strerror (errno));
  287.       return;
  288.     }
  289. #endif
  290.     {
  291.       status = -1;
  292.       wpid = waitpid (pid, &status, 0);
  293.  
  294.       if ((wpid < 0) || !WIFEXITED (status))
  295.     {
  296.       /*  the tmpfile may have been created even if xwd failed  */
  297.       unlink (tmpname);
  298.       g_free (tmpname);
  299.       g_message ("screenshot: xwd didn't work\n");
  300.       return;
  301.     }
  302.     }
  303.  
  304.   /*  now load the tmpfile using the xwd-plug-in  */
  305.   params = gimp_run_procedure ("file_xwd_load",
  306.                    &retvals,
  307.                    GIMP_PDB_INT32, 1,
  308.                    GIMP_PDB_STRING, tmpname,
  309.                    GIMP_PDB_STRING, tmpname,
  310.                    GIMP_PDB_END);
  311.   if (params[0].data.d_status == GIMP_PDB_SUCCESS)
  312.     {
  313.       image_ID = params[1].data.d_image;
  314.     }
  315.   gimp_destroy_params (params, retvals);
  316.  
  317.   /*  get rid of the tmpfile  */
  318.   unlink (tmpname);
  319.   g_free (tmpname);
  320.  
  321.   if (image_ID != -1)
  322.     {
  323.       GimpParasite *parasite;
  324.       gchar *comment;
  325.       
  326.       /*  figure out the monitor resolution and set the image to it  */
  327.       gimp_get_monitor_resolution (&xres, &yres);      
  328.       gimp_image_set_resolution (image_ID, xres, yres);
  329.  
  330.       /*  unset the image filename  */
  331.       gimp_image_set_filename (image_ID, "");
  332.  
  333.       
  334.       /* Set the default comment parasite */
  335.       comment = gimp_gimprc_query ("default-comment");
  336.       
  337.       if (comment != NULL)
  338.         {
  339.           parasite = gimp_parasite_new ("gimp-comment", 
  340.                                         GIMP_PARASITE_PERSISTENT,
  341.                                         strlen (comment) + 1,
  342.                                         comment);
  343.  
  344.           gimp_image_parasite_attach (image_ID, parasite);
  345.           gimp_parasite_free(parasite);
  346.         }
  347.     }
  348.  
  349.   return;
  350. }
  351.  
  352.  
  353. /*  ScreenShot dialog  */
  354.  
  355. static void
  356. shoot_ok_callback (GtkWidget *widget, 
  357.            gpointer   data)
  358. {
  359.   run_flag = TRUE;
  360.   gtk_widget_destroy (GTK_WIDGET (data));
  361. }
  362.  
  363. static gboolean
  364. shoot_dialog (void)
  365. {
  366.   GtkWidget *dialog;
  367.   GtkWidget *main_vbox;
  368.   GtkWidget *frame;
  369.   GtkWidget *vbox;
  370.   GtkWidget *hbox;
  371.   GtkWidget *label;
  372.   GtkWidget *button;
  373.   GtkWidget *decor_button;
  374.   GtkWidget *spinner;
  375.   GtkWidget *sep;
  376.   GSList    *radio_group = NULL;
  377.   GtkObject *adj;
  378.  
  379.  
  380.   gimp_ui_init ("screenshot", FALSE);
  381.  
  382.   /*  main dialog  */
  383.   dialog = gimp_dialog_new (_("Screen Shot"), "screenshot",
  384.                 gimp_standard_help_func, "filters/screenshot.html",
  385.                 GTK_WIN_POS_MOUSE,
  386.                 FALSE, TRUE, FALSE,
  387.  
  388.                 _("OK"), shoot_ok_callback,
  389.                 NULL, NULL, NULL, TRUE, FALSE,
  390.                 _("Cancel"), gtk_widget_destroy,
  391.                 NULL, 1, NULL, FALSE, TRUE,
  392.  
  393.                 NULL);
  394.  
  395.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  396.               GTK_SIGNAL_FUNC (gtk_main_quit),
  397.               NULL);
  398.  
  399.   main_vbox = gtk_vbox_new (FALSE, 4);
  400.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  401.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), main_vbox,
  402.               TRUE, TRUE, 0);
  403.   gtk_widget_show (main_vbox);
  404.  
  405.   /*  single window  */
  406.   frame = gtk_frame_new (_("Grab"));
  407.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  408.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  409.  
  410.   vbox = gtk_vbox_new (FALSE, 2);
  411.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  412.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  413.  
  414.   button = gtk_radio_button_new_with_label (radio_group, _("Single Window"));
  415.   radio_group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));  
  416.   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  417.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  418.               (GtkSignalFunc) gimp_radio_button_update,
  419.               &shootvals.root);
  420.   gtk_object_set_user_data (GTK_OBJECT (button), (gpointer) FALSE);
  421.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), ! shootvals.root);
  422.   gtk_widget_show (button);
  423.  
  424.   /*  with decorations  */
  425.   hbox = gtk_hbox_new (FALSE, 2);
  426.   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
  427.   decor_button =
  428.     gtk_check_button_new_with_label (_("With Decorations"));
  429.   gtk_signal_connect (GTK_OBJECT (decor_button), "toggled",
  430.                       (GtkSignalFunc) gimp_toggle_button_update,
  431.                       &shootvals.decor);
  432.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (decor_button), 
  433.                 shootvals.decor);
  434.   gtk_object_set_data (GTK_OBJECT (button), "set_sensitive", decor_button);
  435.   gtk_box_pack_end (GTK_BOX (hbox), decor_button, FALSE, FALSE, 0);
  436.   gtk_widget_show (decor_button);
  437.   gtk_widget_show (hbox);
  438.  
  439.   sep = gtk_hseparator_new ();
  440.   gtk_box_pack_start (GTK_BOX (vbox), sep, FALSE, FALSE, 0);
  441.   gtk_widget_show (sep);
  442.  
  443.   /*  root window  */
  444.   button = gtk_radio_button_new_with_label (radio_group, _("Whole Screen"));
  445.   radio_group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));  
  446.   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
  447.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  448.               (GtkSignalFunc) gimp_radio_button_update,
  449.               &shootvals.root);
  450.   gtk_object_set_user_data (GTK_OBJECT (button), (gpointer) TRUE);
  451.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), shootvals.root);
  452.   gtk_widget_show (button);
  453.  
  454.   gtk_widget_show (vbox);
  455.   gtk_widget_show (frame);
  456.  
  457.   /*  with delay  */
  458.   hbox = gtk_hbox_new (FALSE, 4);
  459.   gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
  460.   label = gtk_label_new (_("after"));
  461.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  462.   gtk_widget_show (label);
  463.  
  464.   adj = gtk_adjustment_new (shootvals.delay, 0.0, 100.0, 1.0, 5.0, 0.0);
  465.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  466.               gimp_int_adjustment_update, 
  467.               &shootvals.delay);
  468.   spinner = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0);
  469.   gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
  470.   gtk_widget_show (spinner);
  471.  
  472.   label = gtk_label_new (_("Seconds Delay"));
  473.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  474.   gtk_widget_show (label);
  475.  
  476.   gtk_widget_show (hbox);
  477.   gtk_widget_show (dialog);
  478.  
  479.   gtk_main ();
  480.  
  481.   return run_flag;
  482. }
  483.  
  484.  
  485. /*  delay functions  */
  486. void
  487. shoot_delay (gint delay)
  488. {
  489.   gint timeout;
  490.  
  491.   timeout = gtk_timeout_add (1000, shoot_delay_callback, &delay);  gtk_main ();
  492. }
  493.  
  494. gint 
  495. shoot_delay_callback (gpointer data)
  496. {
  497.   gint *seconds_left = (gint *)data;
  498.  
  499.   (*seconds_left)--;
  500.  
  501.   if (!*seconds_left) 
  502.     gtk_main_quit();
  503.  
  504.   return *seconds_left;
  505. }
  506.